package local.blueheart.data.jxmobilemigrate.domino;

import local.blueheart.data.jxmobilemigrate.util.NotesUtil;
import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.NotesFactory;
import lotus.domino.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Enumeration;
import java.util.Vector;

public class DominoSessionPool {

    private static final Logger LOGGER = LoggerFactory.getLogger(DominoSessionPool.class);

    // Domino IIOP URL <servername:port>
    private String dominoServerUrl = "";

    // 用户名
    private String username = "";

    // 密码
    private String password = "";

    //默认连接的nsf数据库
    private String testNSF = "log.nsf";

    //初始Session数
    private int initialSessions = 2;

    //每次增加Session数量
    private int incrementalSessions = 2;

    //池化最大Session数量
    private int maxSessions = 10;

    //池化的 session池
    private Vector<PooledDominoSession> sessions = null;


    public DominoSessionPool(String dominoServerUrl, String username, String password) {
        this.dominoServerUrl = dominoServerUrl;
        this.username = username;
        this.password = password;

    }

    public String getTestNSF() {
        return testNSF;
    }

    public void setTestNSF(String testNSF) {
        this.testNSF = testNSF;
    }

    public int getInitialSessions() {
        return initialSessions;
    }

    public void setInitialSessions(int initialSessions) {
        this.initialSessions = initialSessions;
    }

    public int getIncrementalSessions() {
        return incrementalSessions;
    }

    public void setIncrementalSessions(int incrementalSessions) {
        this.incrementalSessions = incrementalSessions;
    }

    public int getMaxSessions() {
        return maxSessions;
    }

    public void setMaxSessions(int maxSessions) {
        this.maxSessions = maxSessions;
    }

    /**
     * 初始化Domino Session Pool
     */
    public synchronized void createPool() {
        //假如连接池已经创建，保存连接的向量不会为空
        if (sessions != null) {
            return;
        }
        sessions = new Vector<>();
        createSessions(this.initialSessions);
        LOGGER.debug("Session连接池创建成功");
    }

    /**
     * 从Session池中返回一个可用的Session
     *
     * @return Domino Session
     * @throws InterruptedException
     */
    public synchronized Session getSession() {
        if (sessions == null) {
            return null;
        }
        Session session = getFreeSession();
        while (session == null) {
            wait(200);
            session = getFreeSession();
        }
        return session;
    }

    /**
     * Session返回到池中
     *
     * @param session 需要释放的Session
     */
    public synchronized void returnSession(Session session) {
        if (sessions == null) {
            LOGGER.error("连接池为空，无法返回此Session到连接池中");
            return;
        }
        PooledDominoSession pooledDominoSession = null;
        Enumeration<PooledDominoSession> elements = sessions.elements();
        while (elements.hasMoreElements()) {
            pooledDominoSession = elements.nextElement();
            if (session == pooledDominoSession.getSession()) {
                pooledDominoSession.setBusy(false);
                break;
            }
        }
    }

    /**
     * 关闭Session池
     */
    public synchronized void closeSessionPool() {
        if (sessions == null) {
            LOGGER.error("连接池为空，不需要释放");
            return;
        }
        Enumeration<PooledDominoSession> elements = sessions.elements();
        while (elements.hasMoreElements()) {
            PooledDominoSession pooledDominoSession = elements.nextElement();
            if (pooledDominoSession.isBusy()) {

                wait(2000);
                //关闭实际的Session
                closeSession(pooledDominoSession);
                //并从池中删除此包装类
                sessions.remove(pooledDominoSession);
            }
        }
    }

    /**
     * 关闭并释放session
     *
     * @param pooledDominoSession Domino Session对象
     */
    private void closeSession(PooledDominoSession pooledDominoSession) {
        pooledDominoSession.recycle();
    }

    private void wait(int mSeconds) {
        try {
            Thread.sleep(mSeconds);
        } catch (InterruptedException e) {
            LOGGER.error("wait failure", e);
        }
    }


    private Session getFreeSession() {
        Session session = findFreeSession();
        if (session == null) {
            createSessions(incrementalSessions);
            session = findFreeSession();
            if (session == null) {
                return null;
            }
        }
        return session;
    }

    private Session findFreeSession() {
        Session session = null;
        PooledDominoSession pooledDominoSession = null;
        Enumeration<PooledDominoSession> elements = sessions.elements();
        while (elements.hasMoreElements()) {
            pooledDominoSession = elements.nextElement();
            if (!pooledDominoSession.isBusy()) {
                session = pooledDominoSession.getSession();
                pooledDominoSession.setBusy(true);
                if (!testConnection(session)) {
                    try {
                        session = newSession();
                        pooledDominoSession.setSession(session);
                    } catch (NotesException e) {
                       LOGGER.error("new Session Failure",e);
                    }

                }
                break;
            }
        }
        return session;
    }

    private boolean testConnection(Session session) {
        try {
            if (testNSF.isEmpty()) {
                //不用测试默认为可用的
                return true;
            } else {
                //测试Session
                Database db = session.getDatabase("", testNSF);
                NotesUtil.recycle(db);
                return true;
            }
        } catch (Exception e) {
            LOGGER.error("test session failure", e);
            return false;
        }
        //默认连接可用。
    }

    private void createSessions(int numSessions) {
        for (int x = 0; x < numSessions; x++) {
            //是否连接池中的Session数量已经达到最大，到最大不能增加直接退出。
            if (this.maxSessions > 0 && this.sessions.size() >= this.maxSessions) {
                break;
            }
            //增加一个新的session到连接池中
            try {
                sessions.addElement(new PooledDominoSession(newSession()));
            } catch (NotesException e) {
                LOGGER.error("session add failure", e);
            }
            LOGGER.debug("一个Session连接已经创建");
        }

    }

    private Session newSession() throws NotesException {
        //创建一个Session
        Session session;
        session = NotesFactory.createSession(dominoServerUrl, username, password);
        return session;
    }


}
